using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace Colman.Rpg
{
    /// <summary>
    /// Base class for any player or non player characters.
    /// </summary>
    [Serializable]
    public abstract class Character
    {
        internal static Random randomGen = new Random();

        /// <summary>
        /// Creates a new instance of <see cref="Character" />.
        /// </summary>
        /// <param name="name">The character's name.</param>
        protected Character(string name)
        {
            this.name = name;
        }

        /// <summary>
        /// Creates a new instance of <see cref="Character" />.
        /// </summary>
        /// <param name="name">The character's name.</param>
        /// <param name="hp">The current HP of the character.</param>
        /// <param name="maxHP">The maximum HP of the character.</param>
        protected Character(string name, int hp, int maxHP)
        {
            this.name = name;
            this.maxHP = maxHP;
            this.hitPoints = hp;
        }

        #region int HP { get; set; } (hitPoints)
        private int hitPoints;

        /// <summary>
        /// Gets or sets the character's hitpoints.
        /// </summary>
        public int HP
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.hitPoints;
            }
           // [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (this.hitPoints != value)
                {
                    if (value < 0)
                        value = 0;
                    else if (value > this.maxHP)
                        value = this.maxHP;

                    this.hitPoints = value;
                }
            }
        }
        #endregion

        #region string Name { get; } (name)
        private string name;

        /// <summary>
        /// Gets the character's name.
        /// </summary>
        public string Name
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.name;
            }
        }
        #endregion

        #region int AP { get; set; } (attackPoints)
        private int attackPoints;

        /// <summary>
        /// Gets or sets the character's attack points.
        /// </summary>
        public virtual int AP
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.attackPoints;
            }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (this.attackPoints != value)
                {
                    this.attackPoints = value;
                }
            }
        }
        #endregion

        #region int DP { get; set; } (defensePoints)
        private int defensePoints;

        /// <summary>
        /// Gets or sets the character's defense points.
        /// </summary>
        public virtual int DP
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.defensePoints;
            }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (this.defensePoints != value)
                {
                    this.defensePoints = value;
                }
            }
        }
        #endregion

        #region int MaxHP { get; set; } (maxHP)
        private int maxHP;

        /// <summary>
        /// Gets or sets the maximum value of <see cref="HP"/>.
        /// </summary>
        public int MaxHP
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return this.maxHP;
            }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (this.maxHP != value)
                {
                    this.maxHP = value;
                }
            }
        }
        #endregion

        /// <summary>
        /// Gets the combined AP for the character, including all bonuses.
        /// </summary>
        public virtual int CombinedAP
        {
            get
            {
                return this.AP;
            }
        }

        /// <summary>
        /// Gets the combined DP for the character, including all bonuses.
        /// </summary>
        public virtual int CombinedDP
        {
            get
            {
                return this.DP;
            }
        }

        /// <summary>
        /// Attacks an enemy, inflicting damage.
        /// </summary>
        /// <param name="enemy">The enemy to attack.</param>
        /// <returns>The damage inflicted.</returns>
        public virtual int Attack(Character enemy)
        {
            // Check to see that enemy is not null
            if (enemy == null)
                throw new ArgumentNullException("enemy");

            if (this.CombinedAP > enemy.CombinedDP)
            {
                // When attacking, you hit with your attack power, which is blocked by the
                // defense power of the opponent, then changed by luck to be 80%-120% of that power.
                int attackPower = ((int)((this.CombinedAP - enemy.CombinedDP) * (randomGen.Next(8, 12) / 10.0)));
                enemy.HP -= attackPower;

                return attackPower;
            }

            return 0;
        }

        /// <summary>
        /// Defends against an enemy, healing some HP.
        /// </summary>
        /// <param name="enemy">The enemy the character is fighting.</param>
        /// <returns>The amount of HP healed.</returns>
        public virtual int Defend(Character enemy)
        {
            // Check to see that enemy is not null
            if (enemy == null)
                throw new ArgumentNullException("enemy");

            // When defending, you heal 80%-120% of the damage the enemy deals you.
            int healPower = ((int)((enemy.CombinedAP - this.CombinedDP) * (randomGen.Next(8, 12) / 10.0)));

            // If you're against a lesser foe, you heal for 1 point (minimum).
            if (healPower < 0)
                healPower = 1;

            this.HP += healPower;

            return healPower;
        }
    }
}
